﻿/**************************************************************************
创建日期:	2023.1.12
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com

Copyright (c)  ZhCun.cn

描	述： HTTP 操作帮助类
记	录：
***************************************************************************/
using ASmile;
using ASmile.Extend;
using ASmile.Helpers;
using log4net;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace ASmile.Helpers
{
    public class HttpEasy
    {
        public string RootUrl { set; get; }
        /// <summary>
        /// http请求时是响应异常是否抛出，默认为True, 否则返回错误消息
        /// </summary>
        public bool ThrowException { set; get; } = true;
        /// <summary>
        /// 最后一次请求URL地址
        /// </summary>
        protected string LastFullUrl { private set; get; }
        /// <summary>
        /// 最后一次请求的字符串结果(含异常消息)
        /// </summary>
        protected string LastError { private set; get; }
        /// <summary>
        /// 当不抛出异常时，异常处理事件
        /// </summary>
        protected event Action<Exception> OnThrowException;

        protected virtual Encoding DefEncode { set; get; } = Encoding.UTF8;
        /// <summary>
        /// URL （UTF-8）编码
        /// </summary>
        public static string UrlEncode(string url)
        {
            StringBuilder sb = new StringBuilder();
            byte[] byStr = Encoding.UTF8.GetBytes(url); //默认是System.Text.Encoding.Default.GetBytes(str)
            for (int i = 0; i < byStr.Length; i++)
            {
                sb.Append(@"%" + Convert.ToString(byStr[i], 16));
            }

            return sb.ToString();
        }
        /// <summary>
        ///  url 合并
        /// </summary>
        public string UrlJoin(string baseUrl, string url)
        {
            if (baseUrl.IsEmpty()) return url;
            if (url.IsEmpty()) return baseUrl;

            //如果url直接指定了http不进行合并，返回url
            if (url.ToLower().StartsWith("http")) return url;

            if (!baseUrl.EndsWith("/") && !url.StartsWith("/"))
            {
                return $"{baseUrl}/{url}";
            }
            else if (baseUrl.EndsWith("/") && url.StartsWith("/"))
            {
                return $"{baseUrl}{url.Substring(1)}";
            }
            else
            {
                return $"{baseUrl}{url}";
            }
        }

        protected virtual void PrintLog(string log, Exception ex = null)
        {
            //LastFullUrl
            
        }
        /// <summary>
        /// 根据RootUrl 与 参数url 合并为完整url
        /// </summary>
        protected string GetFullUrl(string url)
        {
            return UrlJoin(RootUrl, url);
        }
        /// <summary>
        /// 当请求前触发，设置HttpRequest参数
        /// </summary>
        protected virtual void OnRequest(HttpWebRequest request)
        {
            request.Timeout = 30 * 1000;
            request.ReadWriteTimeout = 30 * 1000;
            request.ContentType = "application/json";
            request.KeepAlive = true;
        }
        /// <summary>
        /// 当请求响应后触发
        /// </summary>
        protected virtual void OnResponse(HttpWebResponse response)
        { }
        /// <summary>
        /// 分页将 byte[] 数据写入流
        /// </summary>
        protected void WriteStream(Stream tarStream, byte[] data, int pageSize = 512)
        {
            var pageCount = data.Length / pageSize;
            for (int i = 0; i < pageCount; i++)
            {
                tarStream.Write(data, (int)tarStream.Position, pageSize);
            }
            var n = data.Length % pageSize;
            if (n > 0)
            {
                tarStream.Write(data, (int)tarStream.Position, n);
            }
        }
        /// <summary>
        /// 读取流，并写新流中，指定每次写入大小
        /// </summary>
        protected void WriteStream(Stream tarStream, Stream srcStream, int bufferSize = 1024)
        {
            if (srcStream == null || tarStream == null) return;
            using (srcStream)
            {
                //创建本地文件写入流
                byte[] bArr = new byte[bufferSize];
                int iTotalSize = 0;
                int size = srcStream.Read(bArr, 0, bArr.Length);
                while (size > 0)
                {
                    iTotalSize += size;
                    tarStream.Write(bArr, 0, size);
                    size = srcStream.Read(bArr, 0, bArr.Length);
                }
            }
        }
        /// <summary>
        /// http 请求
        /// </summary>
        public int Request(string url, string method, Action<HttpWebRequest> SetRequest, Action<Stream> SetRequestStream, Action<HttpWebResponse> SetResponse, Action<Stream> SetResponseStream)
        {
            LastFullUrl = GetFullUrl(url);
            //初始化对像，并设置请求的URL地址
            var request = (HttpWebRequest)WebRequest.Create(LastFullUrl);
            try
            {
                request.Method = method;
                OnRequest(request);
                SetRequest?.Invoke(request);
                Stream requestStream = request.GetRequestStream();
                SetRequestStream?.Invoke(requestStream);
            }
            catch (Exception ex)
            {
                PrintLog("GetRequestStream 时出错", ex);
                if (ThrowException) throw ex;
                LastError = ex.Message;
                return -1;
            }
            HttpWebResponse rsp = null;
            try
            {
                rsp = (HttpWebResponse)request.GetResponse();
                OnResponse(rsp);
                SetResponse?.Invoke(rsp);
                var rspStream = rsp.GetResponseStream();
                SetResponseStream?.Invoke(rspStream);
                return (int)rsp.StatusCode;
            }
            catch (WebException ex)
            {
                PrintLog("GetResponseStream 时出错 1", ex);
                if (ThrowException) throw ex;
                rsp = ex.Response as HttpWebResponse;
                SetResponseStream?.Invoke(rsp.GetResponseStream());
                LastError = rsp.StatusDescription;
                OnThrowException?.Invoke(ex);
                return ((int)rsp.StatusCode);
            }
            catch (Exception e2)
            {
                PrintLog("GetResponseStream 时出错 2", e2);
                if (ThrowException) throw e2;
                LastError = e2.Message;
                OnThrowException?.Invoke(e2);
                return -2;
            }
            finally
            {
                rsp?.Close();
            }
        }
        /// <summary>
        /// post 请求，指定请求
        /// </summary>
        public virtual int Post(string url, string body, Action<HttpWebRequest> SetRequest, Action<Stream> SetRspStreamAction)
        {
            return Request(url, "POST", SetRequest, SetReqStream, null, SetRspStreamAction);

            void SetReqStream(Stream stream)
            {
                if (body.IsEmpty()) return;
                var data = DefEncode.GetBytes(body);
                stream.Write(data, 0, data.Length);
            }
        }
        /// <summary>
        /// 将 POST 内容写入到 fileStream 流, 用于下载文件等
        /// </summary>
        public int Post(string url, string body, Stream stream)
        {
            return Post(url, body, null, (rspStream) =>
            {
                using (stream)
                {
                    WriteStream(stream, rspStream);
                }
            });
        }
        /// <summary>
        /// 发起 POST 请求，返回字符串结果，可设置请求头
        /// </summary>
        public virtual int Post(string url, string body, Action<HttpWebRequest> SetRequest, out string result)
        {
            url = GetFullUrl(url);
            string tmp = "";
            var ret = Post(url, body, SetRequest, (rspStream) =>
            {
                StreamReader sr = new StreamReader(rspStream, DefEncode);
                tmp = sr.ReadToEnd();
            });
            result = tmp;
            return ret;
        }
        /// <summary>
        /// 发起 POST 请求，返回字符串结果
        /// </summary>
        public virtual int Post(string url, string body, out string result)
        {
            url = GetFullUrl(url);
            string tmp = "";
            var ret = Post(url, body, null, (rspStream) =>
            {
                StreamReader sr = new StreamReader(rspStream, DefEncode);
                tmp = sr.ReadToEnd();
            });
            result = tmp;
            return ret;
        }
        /// <summary>
        /// Post方式下载文件
        /// </summary>
        public int PostFile(string url, string body, string fileName)
        {
            FileStream fs = new FileStream(fileName, FileMode.Create);
            return Post(url, body, fs);
        }
        /// <summary>
        /// 上传多个文件，参数名称为 file
        /// </summary>
        public int FileUpload(string url, string[] files, out string result)
        {
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");

            StringBuilder sb = new StringBuilder();
            var ret = Request(url, "POST", SetRequest, SetReqStream, null, SetRepStream);
            result = sb.ToString();
            return ret;

            void SetRequest(HttpWebRequest req)
            {
                req.ContentType = "multipart/form-data; boundary=" + boundary;
                req.Method = "POST";
            }

            void SetReqStream(Stream stream)
            {
                byte[] boundarybytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
                byte[] endbytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");

                string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";
                byte[] buffer = new byte[1024];
                int bytesRead = 0;
                for (int i = 0; i < files.Length; i++)
                {
                    stream.Write(boundarybytes, 0, boundarybytes.Length);
                    string header = string.Format(headerTemplate, "file", Path.GetFileName(files[i]));
                    byte[] headerbytes = DefEncode.GetBytes(header);
                    stream.Write(headerbytes, 0, headerbytes.Length);
                    using (FileStream fileStream = new FileStream(files[i], FileMode.Open, FileAccess.Read))
                    {
                        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            stream.Write(buffer, 0, bytesRead);
                        }
                    }
                }
                stream.Write(endbytes, 0, endbytes.Length);
            }

            void SetRepStream(Stream repStream)
            {
                using (StreamReader sr = new StreamReader(repStream, DefEncode))
                {
                    sb.AppendLine(sr.ReadToEnd());
                }
            }
        }
        /// <summary>
        /// 上传单个文件
        /// </summary>
        public int FileUpload(string url, string file, out string result)
        {
            return FileUpload(url, new string[] { file }, out result);
        }
        /// <summary>
        /// 发起 GET 请求，指定 设置 Request 委托，与响应流委托
        /// </summary>
        protected virtual int Get(string url, Action<HttpWebRequest> SetRequest, Action<Stream> SetRepStreamAction)
        {
            return Request(url, "GET", SetRequest, null, null, SetRepStreamAction);
        }
        /// <summary>
        /// 发起 GET 请求，可指定 设置 Request 委托
        /// </summary>
        protected virtual int Get(string url, Action<HttpWebRequest> SetRequest, out string result)
        {
            string tmp = "";
            var r = Get(url, SetRequest, SetRepStreamAction);
            result = tmp;
            return r;

            void SetRepStreamAction(Stream stream)
            {
                using (StreamReader sr = new StreamReader(stream, DefEncode))
                {
                    tmp = sr.ReadToEnd();
                }
            }
        }
        /// <summary>
        /// 发起 GET 请求 将内容写入到 stream 流, 用于下载文件等
        /// </summary>
        public int Get(string url, Stream stream)
        {
            return Get(url, null, (rspStream) =>
            {
                using (rspStream)
                {
                    WriteStream(stream, rspStream);
                }
            });
        }
        /// <summary>
        /// 下载文件
        /// </summary>
        public int GetFile(string url, string fileName)
        {
            FileStream fs = new FileStream(fileName, FileMode.Create);
            return Get(url, fs);
        }
        /// <summary>
        /// 发起 GET 请求,获取字符串
        /// </summary>
        public int Get(string url, out string result)
        {
            return Get(url, null, out result);
        }
    }
}